#include "gkt_debug.h"
#include "gkt_board.h"
#include "gkt_window.h"

#if !defined(GKT_CONFIG_WINDOW_TIMEOUT_DISABLE) || !GKT_CONFIG_WINDOW_TIMEOUT_DISABLE

#define WINDOW_TIMEOUT_TIMER_ID	\
	(GKT_WINDOW_TIMER_MANAGE | GKT_WINDOW_TIMER_MANAGE_TIMEOUT)

/* timeout */
#if defined(GKT_CONFIG_WINDOW_TIMEOUT_TO_WELCOME)
#define WINDOW_TIMEOUT_TO_WELCOME	GKT_CONFIG_WINDOW_TIMEOUT_TO_WELCOME
#else
#define WINDOW_TIMEOUT_TO_WELCOME	10000
#endif

#if defined(GKT_CONFIG_WINDOW_TIMEOUT_TO_STANDBY) && (GKT_CONFIG_WINDOW_TIMEOUT_TO_STANDBY > 0)
#define WINDOW_TIMEOUT_TO_STANDBY	GKT_CONFIG_WINDOW_TIMEOUT_TO_STANDBY
#elif (WINDOW_TIMEOUT_TO_WELCOME > 0)
#define WINDOW_TIMEOUT_TO_STANDBY	5000
#else
#define WINDOW_TIMEOUT_TO_STANDBY	8000
#endif

#define WINDOW_TIMEOUT_TO_RE_STANDBY	2000
#define WINDOW_TIMEOUT_TO_RE_STANDBY_TIMES	\
	((WINDOW_TIMEOUT_TO_STANDBY + WINDOW_TIMEOUT_TO_RE_STANDBY - 1) / WINDOW_TIMEOUT_TO_RE_STANDBY)

#if (WINDOW_TIMEOUT_TO_WELCOME > 0)

/* timeut type */
enum {
	WINDOW_TIMEOUT_TYPE_NONE = 0,
	WINDOW_TIMEOUT_TYPE_TO_WELCOME,
	WINDOW_TIMEOUT_TYPE_TO_STANDBY,
	WINDOW_TIMEOUT_TYPE_MAX
};

typedef struct _window_timeout {
	int	disable_counter;
	uint32_t	type;	/* timeout type */
	uint32_t	standby_req_counter;
	uint32_t	timer_seq;
} window_timeout_s;
static window_timeout_s s_window_timeout;

static void window_timeout_set(uint32_t type, 
				uint32_t time)
{
	if (WINDOW_TIMEOUT_TYPE_TO_WELCOME == type)
		s_window_timeout.standby_req_counter = 0;

	s_window_timeout.type = type;
	s_window_timeout.timer_seq++;

	gkt_trace("window_timeout: <SET> %u - %u\n", 
		type, s_window_timeout.timer_seq);
	
	gkt_window_timer_set(WINDOW_TIMEOUT_TIMER_ID, 
			time, 0, s_window_timeout.timer_seq);
}

void window_timeout_timer_handler(uint32_t type, 
				uint32_t timer_seq)
{
	int standby_pending;

	gkt_info("window_timeout: <TH> (%u) type(%u)\n",
			s_window_timeout.disable_counter,
			s_window_timeout.type);

	gkt_unused(type);
	if (!s_window_timeout.disable_counter) {
		if (timer_seq == s_window_timeout.timer_seq) {
			standby_pending = gkt_window_system_is_standby_pending();
			if (standby_pending)
				s_window_timeout.type = WINDOW_TIMEOUT_TYPE_TO_STANDBY;

			if (WINDOW_TIMEOUT_TYPE_TO_WELCOME == s_window_timeout.type) {
				gkt_window_show(GKT_WINDOW_ID_START, 0);
				window_timeout_set(WINDOW_TIMEOUT_TYPE_TO_STANDBY, 
						WINDOW_TIMEOUT_TO_STANDBY);
			}
			else if (WINDOW_TIMEOUT_TYPE_TO_STANDBY == s_window_timeout.type) {
				int standby_force;

				standby_force = (s_window_timeout.standby_req_counter >= 
						WINDOW_TIMEOUT_TO_RE_STANDBY_TIMES) ? 1 : 0;
				gkt_window_system_standby(standby_force);
		
				/* if arrive here, does not enter standby mode successfully */
				s_window_timeout.standby_req_counter++;
				window_timeout_set(WINDOW_TIMEOUT_TYPE_TO_STANDBY, 
						WINDOW_TIMEOUT_TO_RE_STANDBY);
			}
		}
		else
			gkt_info("window_timeout: seq unmatch - %u @ %u\n", 
					timer_seq, s_window_timeout.timer_seq);
	}
	else {
		gkt_info("window_timeout: disable_counter(%d)\n", 
			s_window_timeout.disable_counter);
	}
}

static void window_timeout_init_params(uint32_t *type, 
				uint32_t *time)
{
	int is_start = gkt_window_active_is_start();

	if (is_start) {
		*type = WINDOW_TIMEOUT_TYPE_TO_STANDBY;
		*time = WINDOW_TIMEOUT_TO_STANDBY;
	}
	else {
		*type = WINDOW_TIMEOUT_TYPE_TO_WELCOME;
		*time = WINDOW_TIMEOUT_TO_WELCOME;
	}
}

void gkt_window_timeout_reset(void)
{
	uint32_t type, time;

	if (!s_window_timeout.disable_counter) {
		gkt_window_timer_clear(WINDOW_TIMEOUT_TIMER_ID);

		window_timeout_init_params(&type, &time);
		window_timeout_set(type, time);
	}
}

#if GKT_WINDOW_TIMEOUT_DEBUG_ENABLE
void gkt_window_timeout_enable(const char *caller)
{
	uint32_t type, time;

	gkt_trace("window_timeout: <ENABLE> %d - %s\n", 
		s_window_timeout.disable_counter,
		caller ? caller : " ");

	if (s_window_timeout.disable_counter > 0) {
		s_window_timeout.disable_counter--;
		if (!s_window_timeout.disable_counter) {
			window_timeout_init_params(&type, &time);
			window_timeout_set(type, time);
		}
	}
}
#else
void gkt_window_timeout_enable_nc(void)
{
	uint32_t type, time;

	gkt_trace("window_timeout: <ENABLE> %d\n", 
		s_window_timeout.disable_counter);

	if (s_window_timeout.disable_counter > 0) {
		s_window_timeout.disable_counter--;
		if (!s_window_timeout.disable_counter) {
			window_timeout_init_params(&type, &time);
			window_timeout_set(type, time);
		}
	}
}
#endif

void gkt_window_timeout_standby(uint32_t time_ms)
{
	gkt_info("window_timeout: (%d) STANDBY(%u)\n", 
		s_window_timeout.disable_counter, time_ms);

	if ((time_ms > 0) && !s_window_timeout.disable_counter) {
		gkt_window_timer_clear(WINDOW_TIMEOUT_TIMER_ID);
		window_timeout_set(WINDOW_TIMEOUT_TYPE_TO_STANDBY, time_ms);
	}
}

#else

typedef struct _window_timeout {
	int	disable_counter;
	uint32_t	timer_seq;
} window_timeout_s;
static window_timeout_s s_window_timeout;

static void window_timeout_set(uint32_t time)
{
	s_window_timeout.timer_seq++;
	gkt_trace("window_timeout: (%u) interval(%u)\n", 
			s_window_timeout.timer_seq, time);

	gkt_window_timer_set(WINDOW_TIMEOUT_TIMER_ID, 
			time, 0, s_window_timeout.timer_seq);
}

void window_timeout_timer_handler(uint32_t type, 
				uint32_t timer_seq)
{
	gkt_unused(type);

	gkt_trace("window_timeout: (%d) C(%u - %u)\n", 
					s_window_timeout.disable_counter, 
					s_window_timeout.timer_seq, timer_seq);

	if (!s_window_timeout.disable_counter
		&& (timer_seq == s_window_timeout.timer_seq)) 
	{
		gkt_window_system_standby(0);

		/* if arrive here, does not enter standby mode */
		window_timeout_set(WINDOW_TIMEOUT_TO_RE_STANDBY);
	}
}

void gkt_window_timeout_reset(void)
{
	if (!s_window_timeout.disable_counter) {
		gkt_window_timer_clear(WINDOW_TIMEOUT_TIMER_ID);
		window_timeout_set(WINDOW_TIMEOUT_TO_STANDBY);
	}
}

#if GKT_WINDOW_TIMEOUT_DEBUG_ENABLE
void gkt_window_timeout_enable(const char *caller)
{
	gkt_trace("window_timeout: <ENABLE> %d - %s\n", 
		s_window_timeout.disable_counter,
		caller ? caller : " ");

	if (s_window_timeout.disable_counter > 0) {
		s_window_timeout.disable_counter--;
		if (!s_window_timeout.disable_counter)
			window_timeout_set(WINDOW_TIMEOUT_TO_STANDBY);
	}
}
#else
void gkt_window_timeout_enable_nc(void)
{
	gkt_trace("window_timeout: <ENABLE> %d\n", 
		s_window_timeout.disable_counter);

	if (s_window_timeout.disable_counter > 0) {
		s_window_timeout.disable_counter--;
		if (!s_window_timeout.disable_counter)
			window_timeout_set(WINDOW_TIMEOUT_TO_STANDBY);
	}
}
#endif

void gkt_window_timeout_standby(uint32_t time_ms)
{
	if ((time_ms > 0) && !s_window_timeout.disable_counter) {
		gkt_window_timer_clear(WINDOW_TIMEOUT_TIMER_ID);
		window_timeout_set(time_ms);
	}
}

#endif

#if GKT_WINDOW_TIMEOUT_DEBUG_ENABLE
void gkt_window_timeout_disable(const char *caller)
{
	gkt_trace("window_timeout: <DISABLE> %d - %s\n", 
		s_window_timeout.disable_counter, 
		caller ? caller : " ");

	if (!s_window_timeout.disable_counter)
		gkt_window_timer_clear(WINDOW_TIMEOUT_TIMER_ID);
	s_window_timeout.disable_counter++;
}
#else
void gkt_window_timeout_disable_nc(void)
{
	gkt_trace("window_timeout: <DISABLE> %d\n", 
		s_window_timeout.disable_counter);

	if (!s_window_timeout.disable_counter)
		gkt_window_timer_clear(WINDOW_TIMEOUT_TIMER_ID);
	s_window_timeout.disable_counter++;
}
#endif

void gkt_window_timeout_init(void)
{
	s_window_timeout.disable_counter = 1;

	gkt_window_timer_register_manage_handler(
			GKT_WINDOW_TIMER_MANAGE_TIMEOUT,
			window_timeout_timer_handler);
}

#endif

